﻿using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.Searching;
using Sasa.QualityTools.CodeAnalysis.SearchEngine;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.ViewModels.Events;

namespace Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.ViewModels
{
    public class CodeAnalysisResultViewModel : ViewModelBase
    {
        public event EventHandler<CodeFocusRequestedEventArgs> CodeFocusRequested;

        public event EventHandler<ShowRuleRequiredEventArgs> ShowRuleRequired;


        private static readonly int PageSize = 50;

        private int currentPage;

        private int totalPage;

        private int count;

        private bool canGoToPrevPage;

        private bool canGoToNextPage;

        private Func<ProblemViewModel, bool> search;


        public ICollection<ProblemViewModel> Problems { get; private set; }

        public IEnumerable<ProblemViewModel> CurrentPageProblems { get; private set; }

        public IDictionary<string, RuleViewModel> Rules { get; private set; }

        public RelayCommand SearchCommand { get; private set; }

        public RelayCommand GoToFirstPageCommand { get; private set; }

        public RelayCommand GoToPrevPageCommand { get; private set; }

        public RelayCommand GoToNextPageCommand { get; private set; }

        public RelayCommand GoToLastPageCommand { get; private set; }

        public int CurrentPage
        {
            get
            {
                return currentPage;
            }
            private set
            {
                currentPage = value;
                OnPropertyChanged("CurrentPage");
            }
        }

        public int TotalPage
        {
            get
            {
                return totalPage;
            }
            private set
            {
                totalPage = value;
                OnPropertyChanged("TotalPage");
            }
        }

        public int Count
        {
            get
            {
                return count;
            }
            set
            {
                count = value;
                OnPropertyChanged("Count");
            }
        }

        public bool CanGoToPrevPage
        {
            get
            {
                return canGoToPrevPage;
            }
            set
            {
                canGoToPrevPage = value;
                OnPropertyChanged("CanGoToPrevPage");
            }
        }

        public bool CanGoToNextPage
        {
            get
            {
                return canGoToNextPage;
            }
            set
            {
                canGoToNextPage = value;
                OnPropertyChanged("CanGoToNextPage");
            }
        }


        public CodeAnalysisResultViewModel()
        {
            Problems = new List<ProblemViewModel>();
            CurrentPageProblems = new ObservableCollection<ProblemViewModel>();
            Rules = new Dictionary<string, RuleViewModel>();
            SearchCommand = new RelayCommand(o => Search(o as string));
            GoToFirstPageCommand = new RelayCommand(o => GoToFirstPage(), o => CanGoToPrevPage);
            GoToPrevPageCommand = new RelayCommand(o => GoToPrevPage(), o => CanGoToPrevPage);
            GoToNextPageCommand = new RelayCommand(o => GoToNextPage(), o => CanGoToNextPage);
            GoToLastPageCommand = new RelayCommand(o => GoToLastPage(), o => CanGoToNextPage);
        }


        public void FocusRelatedCode(ProblemViewModel problem)
        {
            OnCodeFocusRequested(new CodeFocusRequestedEventArgs(problem));
        }

        public void ShowRule(RuleViewModel rule)
        {
            OnShowRuleRequired(new ShowRuleRequiredEventArgs(rule));
        }

        public void Search(string query)
        {
            if (String.IsNullOrWhiteSpace(query))
            {
                search = null;
            }
            else
            {
                var compiler = new QueryCompiler<ProblemViewModel>(new WordCompiler());
                search = compiler.Compile(query);
            }
            CurrentPage = 1;
            RefreshCurrentPageProblems();
        }

        public void GoToFirstPage()
        {
            CurrentPage = 1;
            RefreshCurrentPageProblems();
        }

        public void GoToPrevPage()
        {
            if (!CanGoToPrevPage) throw new InvalidOperationException("Failed to go to previous page.");
            CurrentPage--;
            RefreshCurrentPageProblems();
        }

        private void GoToNextPage()
        {
            if (!CanGoToNextPage) throw new InvalidOperationException("Failed to go to next page.");
            CurrentPage++;
            RefreshCurrentPageProblems();
        }

        public void GoToLastPage()
        {
            CurrentPage = ((Problems.Count - 1) / PageSize) + 1;
            RefreshCurrentPageProblems();
        }

        protected virtual void OnCodeFocusRequested(CodeFocusRequestedEventArgs e)
        {
            if (CodeFocusRequested != null)
            {
                CodeFocusRequested(this, e);
            }
        }

        protected virtual void OnShowRuleRequired(ShowRuleRequiredEventArgs e)
        {
            if (ShowRuleRequired != null)
            {
                ShowRuleRequired(this, e);
            }
        }

        private void RefreshCurrentPageProblems()
        {
            var problems = (ObservableCollection<ProblemViewModel>)CurrentPageProblems;
            problems.Clear();

            var searchResults = (search == null) ? Problems : Problems.Where(search);

            foreach (var problem in searchResults.Skip((CurrentPage - 1) * PageSize).Take(PageSize))
            {
                problems.Add(problem);
            }

            Count = searchResults.Count();
            TotalPage = (Count - 1) / PageSize + 1;
            CanGoToPrevPage = CurrentPage > 1;
            CanGoToNextPage = CurrentPage < TotalPage;
        }
    }
}
